#!/usr/bin/env python

"""PList utility"""

import argparse
import os
import subprocess
import re
import unicodedata


def normalize_char(c):
    try:
        UNICODE_EXISTS = bool(type(unicode))
    except NameError:
        unicode = lambda s: str(s)
    try:
        cname = unicodedata.name(unicode(c))
        cname = cname[: cname.index(" WITH")]
        return unicodedata.lookup(cname)
    except (ValueError, KeyError):
        return c


def normalize_string(s):
    return "".join(normalize_char(c) for c in s)


def replace_var(str, var, val):
    if str.find("$(" + var + ")") != -1:
        return str.replace("$(" + var + ")", val)
    if str.find("${" + var + "}") != -1:
        return str.replace("${" + var + "}", val)
    return str


parser = argparse.ArgumentParser(description="PList utility for Ninja builds")
parser.add_argument(
    "files", metavar="file", type=open, nargs="+", help="Source plist file"
)
parser.add_argument("--exename", type=str, help="Executable name", default=[])
parser.add_argument("--prodname", type=str, help="Product name", default=[])
parser.add_argument("--bundle", type=str, help="Bundle identifier", default=[])
parser.add_argument("--output", type=str, help="Output path", default=[])
parser.add_argument("--target", type=str, help="Target OS", default=[])
parser.add_argument(
    "--deploymenttarget", type=str, help="Target OS version", default=[]
)
options = parser.parse_args()

if not options.exename:
    options.exename = "unknown"
if not options.prodname:
    options.prodname = "unknown"
if not options.target:
    options.target = "macos"
if not options.deploymenttarget:
    if options.target == "macos":
        options.deploymenttarget = "12.0"
    else:
        options.deploymenttarget = "10.0"

buildversion = subprocess.check_output(["sw_vers", "-buildVersion"]).decode().strip()

# Merge input plists using first file as base
lines = []
for f in options.files:
    _, extension = os.path.splitext(f.name)
    if extension != ".plist":
        continue
    if lines == []:
        lines += [line.strip("\n\r") for line in f]
    else:
        mergelines = [line.strip("\n\r") for line in f]
        for i in range(0, len(mergelines)):
            if re.match("^<dict/>$", mergelines[i]):
                break
            if re.match("^<dict>$", mergelines[i]):
                for j in range(0, len(lines)):
                    if re.match("^</dict>$", lines[j]):
                        for k in range(i + 1, len(mergelines)):
                            if re.match("^</dict>$", mergelines[k]):
                                break
                            lines.insert(j + (k - (i + 1)), mergelines[k])
                        break
                break

# Parse input plist to get package type and signature
bundle_package_type = "APPL"
bundle_signature = "????"

for i in range(0, len(lines)):
    if "CFBundlePackageType" in lines[i]:
        match = re.match("^.*>(.*)<.*$", lines[i + 1])
        if match:
            bundle_package_type = match.group(1)
    if "CFBundleSignature" in lines[i]:
        match = re.match("^.*>(.*)<.*$", lines[i + 1])
        if match:
            bundle_signature = match.group(1)

# Write package type and signature to PkgInfo in output path
with open(
    os.path.join(os.path.dirname(options.output), "PkgInfo"), "w"
) as pkginfo_file:
    pkginfo_file.write(bundle_package_type + bundle_signature)
    pkginfo_file.close()

# insert os version
for i in range(0, len(lines)):
    if re.match("^<dict>$", lines[i]):
        lines.insert(i + 1, "\t<key>BuildMachineOSBuild</key>")
        lines.insert(i + 2, "\t<string>" + buildversion + "</string>")
        break

# replace build variables name
for i in range(0, len(lines)):
    lines[i] = replace_var(lines[i], "EXECUTABLE_NAME", options.exename)
    lines[i] = replace_var(lines[i], "PRODUCT_NAME", options.prodname)
    lines[i] = replace_var(
        lines[i],
        "PRODUCT_NAME:rfc1034identifier",
        normalize_string(options.exename).lower(),
    )
    lines[i] = replace_var(
        lines[i],
        "PRODUCT_NAME:c99extidentifier",
        normalize_string(options.exename).lower().replace("-", "_").replace(".", "_"),
    )
    lines[i] = replace_var(lines[i], "IOS_DEPLOYMENT_TARGET", options.deploymenttarget)
    lines[i] = replace_var(
        lines[i], "MACOSX_DEPLOYMENT_TARGET", options.deploymenttarget
    )

# replace bundle identifier if given
if not options.bundle is None and options.bundle != "":
    for i in range(0, len(lines)):
        if lines[i].find("CFBundleIdentifier") != -1:
            lines[i + 1] = "\t<string>" + normalize_string(options.bundle) + "</string>"
            break

# add supported platform, minimum os version and requirements
if options.target == "ios":
    for i in range(0, len(lines)):
        if "CFBundleSignature" in lines[i]:
            lines.insert(i + 2, "\t<key>CFBundleSupportedPlatforms</key>")
            lines.insert(i + 3, "\t<array>")
            lines.insert(i + 4, "\t\t<string>iPhoneOS</string>")
            lines.insert(i + 5, "\t</array>")
            lines.insert(i + 6, "\t<key>MinimumOSVersion</key>")
            lines.insert(i + 7, "\t<string>6.0</string>")
            lines.insert(i + 8, "\t<key>UIDeviceFamily</key>")
            lines.insert(i + 9, "\t<array>")
            lines.insert(i + 10, "\t\t<integer>1</integer>")
            lines.insert(i + 11, "\t\t<integer>2</integer>")
            lines.insert(i + 12, "\t</array>")
            break

# add build info
# <key>DTCompiler</key>
# <string>com.apple.compilers.llvm.clang.1_0</string>
# <key>DTPlatformBuild</key>
# <string>12B411</string>
# <key>DTPlatformName</key>
# <string>iphoneos</string>
# <key>DTPlatformVersion</key>
# <string>8.1</string>
# <key>DTSDKBuild</key>
# <string>12B411</string>
# <key>DTSDKName</key>
# <string>iphoneos8.1</string>
# <key>DTXcode</key>
# <string>0611</string>
# <key>DTXcodeBuild</key>
# <string>6A2008a</string>

# write final Info.plist in output path
with open(options.output, "w") as plist_file:
    for line in lines:
        # print lines[i]
        plist_file.write(line + "\n")
    plist_file.close()

# run plutil -convert binary1
sdk = "iphoneos"
platformpath = (
    subprocess.check_output(["xcrun", "--sdk", sdk, "--show-sdk-platform-path"])
    .decode()
    .strip()
)
localpath = (
    platformpath
    + "/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin"
)
plutil = (
    "PATH="
    + localpath
    + " "
    + subprocess.check_output(["xcrun", "--sdk", sdk, "-f", "plutil"]).decode().strip()
)
plcommand = plutil + " -convert binary1 " + options.output
os.system(plcommand)
